﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using GalaSoft.MvvmLight;
using System.Collections.ObjectModel;

namespace CochonMVVM.Commandes
{
    public class Item
    {
        public DateTime Date { get; set; }
        public double Value { get; set; }
    }

    public class CommandsViewModel : ViewModelBase
    {
        #region Variables privées de calcul

        private static CochonMCDDataContext dataContext = new CochonMCDDataContext();
        private DateTime dateLimite = DateTime.MinValue;
        private bool nettoyerMachine = false;
        private double stockTotal = 0;
        private double stockTotalSec = 0;
        private double stockTotalNoix = 0;

        #endregion Variables privées de calcul

        #region Propiétés calculées liées aux Stocks

        private ObservableCollection<Item> stockMoyen = new ObservableCollection<Item>();
        public ObservableCollection<Item> StockMoyen
        {
            get { return stockMoyen; }
            set
            {
                if (stockMoyen != value)
                {
                    stockMoyen = value;
                    RaisePropertyChanged(() => StockMoyen);
                }
            }
        }

        private ObservableCollection<Item> stockMoyenSec = new ObservableCollection<Item>();
        public ObservableCollection<Item> StockMoyenSec
        {
            get { return stockMoyenSec; }
            set
            {
                if (stockMoyenSec != value)
                {
                    stockMoyenSec = value;
                    RaisePropertyChanged(() => StockMoyenSec);
                }
            }
        }

        private ObservableCollection<Item> stockMoyenNoix = new ObservableCollection<Item>();
        public ObservableCollection<Item> StockMoyenNoix
        {
            get { return stockMoyenNoix; }
            set
            {
                if (stockMoyenNoix != value)
                {
                    stockMoyenNoix = value;
                    RaisePropertyChanged(() => StockMoyenNoix);
                }
            }
        }

        private ObservableCollection<Item> stocksCollection = new ObservableCollection<Item>();
        public ObservableCollection<Item> StocksCollection
        {
            get { return stocksCollection; }
            set
            {
                if (stocksCollection != value)
                {
                    stocksCollection = value;
                    RaisePropertyChanged(() => StocksCollection);
                }
            }
        }

        private ObservableCollection<Item> stocksSecCollection = new ObservableCollection<Item>();
        public ObservableCollection<Item> StocksSecCollection
        {
            get { return stocksSecCollection; }
            set
            {
                if (stocksSecCollection != value)
                {
                    stocksSecCollection = value;
                    RaisePropertyChanged(() => StocksSecCollection);
                }
            }
        }

        private ObservableCollection<Item> stocksNoixCollection = new ObservableCollection<Item>();
        public ObservableCollection<Item> StocksNoixCollection
        {
            get { return stocksNoixCollection; }
            set
            {
                if (stocksNoixCollection != value)
                {
                    stocksNoixCollection = value;
                    RaisePropertyChanged(() => StocksNoixCollection);
                }
            }
        }

        #endregion Propiétés liées aux Stocks

        #region Propriétés saisissables par l'utilisateur

        private DateTime dateDebut = new DateTime(2011, 1, 1);

        public DateTime DateDebut
        {
            get { return dateDebut; }
            set
            {
                if (dateDebut != value)
                {
                    dateDebut = value;
                    RaisePropertyChanged(() => dateDebut);
                }
            }
        }

        private DateTime dateToEvaluate = DateTime.Now;

        public DateTime DateToEvaluate
        {
            get { return dateToEvaluate; }
            set
            {
                if (dateToEvaluate != value)
                {
                    dateToEvaluate = value;
                    dateToEvaluate = dateToEvaluate.AddHours(23);
                    dateToEvaluate = dateToEvaluate.AddMinutes(59);
                    RaisePropertyChanged(() => DateToEvaluate);
                }
            }
        }

        private double majoration;

        public double Majoration
        {
            get { return majoration; }
            set
            {
                if (majoration != value)
                {
                    majoration = value;
                    RaisePropertyChanged(() => Majoration);
                }
            }
        }

        private string pjs = string.Empty;

        public string PJS
        {
            get { return pjs; }
            set
            {
                if (pjs != value)
                {
                    pjs = value;
                    RaisePropertyChanged(() => PJS);
                }
            }
        }

        private string pjsn = string.Empty;

        public string PJSN
        {
            get { return pjsn; }
            set
            {
                if (pjsn != value)
                {
                    pjsn = value;
                    RaisePropertyChanged(() => PJSN);
                }
            }
        }

        private string pms = string.Empty;

        public string PMS
        {
            get { return pms; }
            set
            {
                if (pms != value)
                {
                    pms = value;
                    RaisePropertyChanged(() => PMS);
                }
            }
        }

        private string pmsn = string.Empty;

        public string PMSN
        {
            get { return pmsn; }
            set
            {
                if (pmsn != value)
                {
                    pmsn = value;
                    RaisePropertyChanged(() => PMSN);
                }
            }
        }

        private string qteSec = string.Empty;

        public string QteSec
        {
            get { return qteSec; }
            set
            {
                if (qteSec != value)
                {
                    qteSec = value;

                    RaisePropertyChanged(() => QteSec);
                }
            }
        }

        private string qteNoix = string.Empty;

        public string QteNoix
        {
            get { return qteNoix; }
            set
            {
                if (qteNoix != value)
                {
                    qteNoix = value;
                    RaisePropertyChanged(() => QteNoix);
                }
            }
        }

        #endregion Propriétés saisissables par l'utilisateur

        #region Commandes

        private DelegateCommand<object> calculerProductionCommand;

        public ICommand CalculerProductionCommand
        {
            get { return calculerProductionCommand; }
        }

        #endregion Commandes

        #region InitializeData

        /// <summary>
        /// Récupération des commandes existants en BDD
        /// </summary>
        private void InitializeData()
        {
            PJS = dataContext.Prevision.FirstOrDefault(pjs => pjs.Nom == "PJS").Valeur.ToString();
            PJSN = dataContext.Prevision.FirstOrDefault(pjsn => pjsn.Nom == "PJSN").Valeur.ToString();
            PMS = dataContext.Prevision.FirstOrDefault(pms => pms.Nom == "PMS").Valeur.ToString();
            PMSN = dataContext.Prevision.FirstOrDefault(pmsn => pmsn.Nom == "PMSN").Valeur.ToString();
            QteSec = dataContext.Stock.FirstOrDefault(sec => sec.ArticleId == dataContext.Article.FirstOrDefault(art => art.Designation == "Saucisson sec").Id).Qte.ToString();
            QteNoix = dataContext.Stock.FirstOrDefault(sec => sec.ArticleId == dataContext.Article.FirstOrDefault(art => art.Designation == "Saucisson sec aux noix").Id).Qte.ToString();
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public CommandsViewModel()
        {
            InitializeData();

            calculerProductionCommand = new DelegateCommand<object>(
                obj =>
                {
                    // calculer quand le stock sera < 0
                    // A = production journalière (sec + noix) * nb jours - qte prévision commande
                    // A + qte stock --> qte restante en stock à terme des nb jours
                    // Penser à augmenter les commandes de 20% en juillet & aout
                    // ex : 15 jours en juillet: 
                    // Tester si on passe par juillet et par aout.
                    stockTotalSec = double.Parse(qteSec);
                    stockTotalNoix = double.Parse(qteNoix);
                    stockTotal = double.Parse(qteNoix) + double.Parse(qteSec);

                    int compteurSechage = 0;
                    double productionEnCoursDeSechage = 0;
                    double productionSecEnCoursDeSechage = 0;
                    double productionNoixEnCoursDeSechage = 0;
                    DateTime dateDeDebut = DateDebut;
                    nettoyerMachine = false;

                    stocksCollection.Clear();
                    stocksSecCollection.Clear();
                    stocksNoixCollection.Clear();
                    stockMoyen.Clear();
                    stockMoyenSec.Clear();
                    stockMoyenNoix.Clear();

                    stocksCollection.Add(new Item
                    {
                        Value = stockTotal,
                        Date = dateDebut
                    });
                    stocksSecCollection.Add(new Item
                    {
                        Value = stockTotalSec,
                        Date = dateDebut
                    });
                    stocksNoixCollection.Add(new Item
                    {
                        Value = stockTotalNoix,
                        Date = dateDebut
                    });

                    int nbMois = (dateToEvaluate.Year - dateDeDebut.Year) * 12 + dateToEvaluate.Month - dateDeDebut.Month + 1;
                    double prevMensSec = double.Parse(pms) + double.Parse(pms) * (Majoration / 100);
                    double prevMensNoix = double.Parse(pmsn) + double.Parse(pmsn) * (Majoration / 100);

                    for (int i = 0; i < nbMois; i++)
                    {
                        //if (stockTotal <= 0 && dateLimite == DateTime.MinValue)
                        //{
                        //    dateLimite = dateDeDebut;
                        //}

                        if (compteurSechage > 0)
                        {
                            // on ajoute la production du mois dernier dans le stock
                            stockTotal += productionEnCoursDeSechage;
                            stockTotalSec += productionSecEnCoursDeSechage;
                            stockTotalNoix += productionNoixEnCoursDeSechage;
                        }

                        if (i == nbMois - 1)
                        {
                            evaluateProduction(dateDeDebut, dateToEvaluate, prevMensSec, prevMensNoix,
                                ref productionEnCoursDeSechage, ref productionSecEnCoursDeSechage, ref productionNoixEnCoursDeSechage);
                        }
                        else
                        {
                            evaluateProduction(dateDeDebut, GetLastDayOfThisMonth(dateDeDebut), prevMensSec, prevMensNoix,
                                ref productionEnCoursDeSechage, ref productionSecEnCoursDeSechage, ref productionNoixEnCoursDeSechage);
                        }


                        compteurSechage++;

                        // On supprime les heures et minutes de notre date pour qu'elle commence à minuit
                        DateTime temp = GetLastDayOfThisMonth(dateDeDebut).AddDays(1);
                        dateDeDebut = new DateTime(temp.Year, temp.Month, temp.Day);
                    }

                    double stockMoy = stocksCollection.Average(x => x.Value);
                    double stockSecMoy = stocksSecCollection.Average(x => x.Value);
                    double stockNoixMoy = stocksNoixCollection.Average(x => x.Value);

                    StockMoyen.Add(new Item { Value = stockMoy, Date = DateDebut });
                    StockMoyen.Add(new Item { Value = stockMoy, Date = DateToEvaluate });
                    StockMoyenSec.Add(new Item { Value = stockSecMoy, Date = DateDebut });
                    StockMoyenSec.Add(new Item { Value = stockSecMoy, Date = DateToEvaluate });
                    StockMoyenNoix.Add(new Item { Value = stockNoixMoy, Date = DateDebut });
                    StockMoyenNoix.Add(new Item { Value = stockNoixMoy, Date = DateToEvaluate });
                    RaisePropertyChanged(() => StocksCollection);
                    RaisePropertyChanged(() => StocksSecCollection);
                    RaisePropertyChanged(() => StocksNoixCollection);
                    RaisePropertyChanged(() => StockMoyen);
                    RaisePropertyChanged(() => StockMoyenSec);
                    RaisePropertyChanged(() => StockMoyenNoix);

                    string messageFinal = "Les stocks restant à la date indiquée sont de : \n\n"
                                        + (int)Math.Floor(stockTotalSec) + " saucissons secs et\n"
                                        + (int)Math.Floor(stockTotalNoix) + " saucissons secs au noix\n"
                                        + "\nsoit un stock total de " + (int)Math.Floor(stockTotal) + " saucissons\n";
                    messageFinal += "\n\nLes stocks moyens sur la période sont de : \n\n"
                                        + (int)Math.Floor(stockSecMoy) + " saucissons secs et\n"
                                        + (int)Math.Floor(stockNoixMoy) + " saucissons secs au noix\n"
                                        + "\nsoit un stock moyen total de " + (int)Math.Floor(stockMoy) + " saucissons\n";
                    MessageBox.Show(messageFinal);
                });

            // stock moyen sur l'année = addition stock de chaque jour / nb jours
        }

        private static DateTime GetLastDayOfThisMonth(DateTime date)
        {
            DateTime now = date;
            int nbDays = DateTime.DaysInMonth(now.Year, now.Month);

            return new DateTime(now.Year, now.Month, nbDays, 23, 59, 59, 999);
        }

        private void evaluateProduction
            (DateTime dated, DateTime datef, double prevMensSec, double prevMensNoix, ref double productionMoisTotal, ref double productionMoisSec, ref double productionMoisNoix)
        {
            double majorationEte = 1;
            int nbWorkingDays = NbWorkingDays(dated, datef);

            // Gestion du nettoyage de la machine qui prend 3h par mois
            if (nettoyerMachine)
            {
                nbWorkingDays--;
                nettoyerMachine = false;
            }
            else
            {
                nettoyerMachine = true;
            }

            // Gestion de la majoration des commandes de 20% en été
            if (dated.Month == 7 || dated.Month == 8)
            {
                majorationEte = 1.2;
            }

            // On met à jour les stocks en fonction des commandes
            stockTotal =
                // stockTotal
                stockTotal
                // commandes éventuellement majorése à 20% * nb jours juillet aout
                - (((prevMensSec * majorationEte + prevMensNoix * majorationEte)
                / NbWorkingDays(new DateTime(dated.Year, dated.Month, 1), GetLastDayOfThisMonth(dated))) * nbWorkingDays);

            // On met à jour les stocks en fonction des commandes
            stockTotalSec =
                // stock saucissons sec
                stockTotalSec
                // commandes éventuellement majorées à 20% * nb jours juillet aout
                - (((prevMensSec * majorationEte)
                / NbWorkingDays(new DateTime(dated.Year, dated.Month, 1), GetLastDayOfThisMonth(dated))) * nbWorkingDays);

            // On met à jour les stocks en fonction des commandes
            stockTotalNoix =
                // stock saucissons noix
                stockTotalNoix
                // commandes éventuellement majorées à 20% * nb jours juillet aout
                - (((prevMensNoix * majorationEte)
                / NbWorkingDays(new DateTime(dated.Year, dated.Month, 1), GetLastDayOfThisMonth(dated))) * nbWorkingDays);

            StocksCollection.Add(new Item
            {
                Value = stockTotal,
                Date = datef
            });

            StocksSecCollection.Add(new Item
            {
                Value = stockTotalSec,
                Date = datef
            });

            StocksNoixCollection.Add(new Item
            {
                Value = stockTotalNoix,
                Date = datef
            });

            // on retourne les production du mois (sec, noix et total)
            // production journalière * nb de jours ouvrés jusqu'à la date indiquée

            productionMoisTotal = (double.Parse(PJS) + double.Parse(PJSN)) * nbWorkingDays;
            productionMoisSec = double.Parse(PJS) * nbWorkingDays;
            productionMoisNoix = double.Parse(PJSN) * nbWorkingDays;
        }

        //double stockRestantALaDateIndiquee =
        //    // production journalière * nb de jours ouvrés jusqu'à la date indiquée
        //    (double.Parse(PJS) + double.Parse(PJSN)) * nbWorkingDays
        //    // commandes éventuellement majorése à 20% * nb jours juillet aout
        //    - (((double.Parse(PMS) * majoration + double.Parse(PMSN) * majoration) 
        //    / NbWorkingDays(new DateTime(dated.Year, dated.Month, 1), GetLastDayOfThisMonth(dated))) * nbWorkingDays);
        //    // On prend en compte les stocks existants
        //    //+ stockTotal;

        private int nbJoursJuilletAout(DateTime dateDeb, DateTime dateFin)
        {
            List<int> monthNumbersCollection = new List<int>();
            DateTime day = dateDeb;
            while (day <= dateFin)
            {
                if (day.DayOfWeek != DayOfWeek.Saturday && day.DayOfWeek != DayOfWeek.Sunday)
                {
                    monthNumbersCollection.Add(day.Month);
                }
                day = day.AddDays(1);
            }

            return monthNumbersCollection.Count(x => (x == 7 || x == 8));
        }

        public static int NbWorkingDays(DateTime startDate, DateTime endDate)
        {
            if (endDate <= startDate) { return 0; }

            int nbDays = 0;
            while (endDate > startDate && endDate.DayOfWeek != DayOfWeek.Sunday && endDate.DayOfWeek != DayOfWeek.Saturday)
            {
                endDate = endDate.AddDays(-1);
                nbDays++;
            }

            while (startDate < endDate && startDate.DayOfWeek != DayOfWeek.Saturday && startDate.DayOfWeek != DayOfWeek.Sunday)
            {
                startDate = startDate.AddDays(1);
                nbDays++;
            }

            TimeSpan totalNbDaysSpan = endDate - startDate;

            nbDays += 5 * (totalNbDaysSpan.Days / 7);
            return nbDays;

        }
    }
}
